#region System
using System;
using System.Collections.Generic;
using System.IO;
using CF.Framework.Engine.Drawing;
using CF.Framework.Engine.Misc;
using CF.Framework.Engine.Screens;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion

namespace CF.Framework.Engine.Controls
{
	public enum MenuDirection
	{
		LeftToRight,
		RightToLeft,
		TopToBottom,
		BottomToTop,
		CenterToTopAndBottom,
		CenterToLeftAndRight,
	}

	public enum MenuDock
	{
		TopLeft = 1,
		TopCenter = 2,
		TopRight = 4,
		MiddleLeft = 16,
		MiddleCenter = 32,
		MiddleRight = 64,
		BottomLeft = 256,
		BottomCenter = 512,
		BottomRight = 1024,
	}

	public class Menu : Control
	{
		public SpriteFont Font { get; set; }
		public MenuDirection Direction { get; set; }
		public MenuDock Dock { get; set; }
		public Color Color { get; set; }
		public Color SelectedColor { get; set; }
		public int Padding { get; set; }

		public List<MenuItem> Items { get; private set; }
		public int SelectedIndex { get; private set; }

		public Menu()
		{
			this.Items = new List<MenuItem>();

			this.Font = GameApp.Current.ScreenManager.SpriteFonts.LargeFont;
			this.Padding = 10;
			this.Direction = MenuDirection.CenterToTopAndBottom;
			this.Dock = MenuDock.MiddleCenter;
			this.SelectedIndex = -1;
		}

		/// <summary>
		/// Queries how much space this menu entry requires.
		/// </summary>
		internal virtual int GetHeight()
		{
			return Font.LineSpacing;
		}

		/// <summary>
		/// Queries how wide the entry is, used for centering on the screen.
		/// </summary>
		internal virtual int GetWidth(string text)
		{
			return (int)Font.MeasureString(text).X;
		}

		#region Handle Input

		/// <summary>
		/// Allows the screen to create the hit bounds for a particular menu entry.
		/// </summary>
		protected virtual Rectangle GetMenuEntryHitBounds(MenuItem item)
		{
			// the hit bounds are the entire width of the screen, and the height of the entry
			// with some additional padding above and below.
			return new Rectangle(
				(int)item.Position.X - Padding,
				(int)item.Position.Y - Padding / 2 - (int)item.Size.Y / 2,
				(int)item.Size.X + Padding,
				(int)item.Size.Y + Padding / 2);
		}

		/// <summary>
		/// Responds to user input, changing the selected entry and accepting
		/// or cancelling the menu.
		/// </summary>
		protected override void OnHandleInput(InputState input)
		{
			//// look for any taps that occurred and select any entries that were tapped
			//foreach (GestureSample gesture in input.)
			//{
			//    if (gesture.GestureType == GestureType.Tap)
			//    {
			//        // convert the position to a Point that we can test against a Rectangle
			//        Point tapLocation = new Point((int)gesture.Position.X, (int)gesture.Position.Y);

			//        // iterate the entries to see if any were tapped
			//        for (int pos = 0; pos < Items.Count; pos++)
			//        {
			//            MenuItem item = Items[pos];

			//            if (GetMenuEntryHitBounds(item).Contains(tapLocation))
			//            {
			//                // select the entry. since gestures are only available on Windows Phone,
			//                // we can safely pass PlayerIndex.One to all entries since there is only
			//                // one player on Windows Phone.
			//                if (SelectedIndex == pos)
			//                {
			//                    item.DoSelected();
			//                }
			//                else
			//                {
			//                    SelectedIndex = pos;
			//                }
			//            }
			//        }
			//    }
			//}
		}

		#endregion

		#region Update and Draw
		/// <summary>
		/// Updates the menu.
		/// </summary>
		protected override void OnUpdate(GameTime gameTime)
		{
			base.OnUpdate(gameTime);

			// Update each nested MenuEntry object.
			for (int pos = 0; pos < Items.Count; pos++)
			{
				MenuItem item = Items[pos];

				bool isSelected = IsActive && (pos == SelectedIndex);

				// When the menu selection changes, entries gradually fade between
				// their selected and deselected appearance, rather than instantly
				// popping to the new state.
				float fadeSpeed = (float)gameTime.ElapsedGameTime.TotalSeconds * 4;

				if (isSelected)
				{
					item.SelectionFade = Math.Min(item.SelectionFade + fadeSpeed, 1);
				}
				else
				{
					item.SelectionFade = Math.Max(item.SelectionFade - fadeSpeed, 0);
				}

				// Pulsate the size of the selected menu entry.
				double time = gameTime.TotalGameTime.TotalSeconds;

				float pulsate = (float)Math.Sin(time * 6) + 1;

				item.Scale = 1 + pulsate * 0.02f * item.SelectionFade;

				// Make the menu slide into place during transitions, using a
				// power curve to make things look more interesting (this makes
				// the movement slow down as it nears the end).
				float transitionOffset = (float)Math.Pow(TransitionPosition, 2);

				// start at Y = 175; each X value is generated per entry
				item.Size = new Vector2(GetWidth(item.Text), GetHeight());

				Vector2 position = new Vector2(0f, 175f);

				position.X = ScreenManager.ScreenWidth / 2 - item.Size.X / 2;
				position.Y += item.Size.Y * pos + Padding * pos;

				// set the entry's position
				item.Position = position;
			}
		}

		protected override void OnDraw(GameTime gameTime, SpriteBatch spriteBatch)
		{
			base.OnDraw(gameTime, spriteBatch);

			// Draw each menu entry in turn.
			for (int pos = 0; pos < Items.Count; pos++)
			{
				MenuItem item = Items[pos];

				bool isSelected = IsActive && (pos == SelectedIndex);

				// Draw the selected entry in yellow, otherwise white.
				Color color = isSelected ? SelectedColor : Color;

				// Modify the alpha to fade text out during transitions.
				color = new Color(color.R, color.G, color.B) * TransitionAlpha;

				// Draw text, centered on the middle of each line.
				SpriteFont font = Font;

				Vector2 origin = new Vector2(0, font.LineSpacing / 2);

				Vector2 newSize = new Vector2(item.Size.X * item.Scale, item.Size.Y * item.Scale);
				Vector2 newPosition = new Vector2(item.Position.X - (newSize.X - item.Size.X) / 2, item.Position.Y - (newSize.Y - item.Size.Y) / 2);

				spriteBatch.DrawString(font, item.Text, newPosition, color, 0, origin, item.Scale, SpriteEffects.None, 0);
			}
		}

		#endregion
	}
}
